This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

#Load some useful libraries
library(limma)
library(DESeq2)
library(edgeR)
library(data.table)
library(tidyr)
library(magrittr)
library(tidyverse)
library(sjlabelled)
library(sjmisc)
library(ggplot2)
library(compositions)
library(broom)
library(DataCombine)
library(ggsignif)
library(ggpubr)
#import data
countdf <- read.csv(file = 'labs.csv')
rownames(countdf) <- NULL
meta <- read.csv(file = 'labs_metadata.csv')
rownames(meta) <- NULL
full <- read.csv(file = 'labs_full.csv')
rownames(full) <- NULL

#clean-up tables
#prepare for model
#make consistent index/column/label/ordering between tables/matrices
names(countdf)[1] <- 'name'
countdf[countdf==0] <- 0.0000001
names(countdf) <- sapply(str_remove_all(names(countdf),"X"),"[") # remove added X's from PCI names
count_matrix <- countdf
count_matrix <- count_matrix[,2:ncol(count_matrix)]
count_matrix <- as.matrix(count_matrix)
names(full) <- sapply(str_remove_all(colnames(full),"X"),"[")
names(count_matrix) <- sapply(str_remove_all(colnames(count_matrix),"X"),"[")
meta$sex <- factor(meta$sex)
full$sex <- factor(full$sex)
meta$bowel <- factor(as.numeric(factor(meta$bowel, levels = c(1,2,3,4), labels = c(1,2,3,4))))
full$bowel <- factor(as.numeric(factor(full$bowel, levels = c(1,2,3,4), labels = c(1,2,3,4))))
meta <- within(meta, bowel <- relevel(bowel, ref = 3))
full <- within(full, bowel <- relevel(bowel, ref = 3))
as.data.frame(count_matrix)
countdf <- DropNA(countdf)
No Var specified. Dropping all NAs from the data frame.

0 rows dropped from the data frame because of missing values.
meta <- DropNA(meta)
No Var specified. Dropping all NAs from the data frame.

0 rows dropped from the data frame because of missing values.
full <- DropNA(full)
No Var specified. Dropping all NAs from the data frame.

0 rows dropped from the data frame because of missing values.
countdf
meta
full
#design GLM using lmFit and eBayes
#Algorithm provided by and adapted from Christian Diener, PhD:
###########################################################################################################
design <- model.matrix(~bowel + sex + age + BMI_CALC + eGFR, full)  # note the same formula as in corncob
dge <- DGEList(counts=count_matrix)  # where `count_matrix` is the matrix mentioned above
#dge <- calcNormFactors(dge)  # Normalize the matrix (this step only for CORNCOB/microbiome data)
logCPM <- cpm(dge, log=TRUE)  # takes the log of the data
fit <- lmFit(logCPM, design)  # fits the model for all labs
fit <- eBayes(fit)  # stabilizes the variances
###########################################################################################################
#Pre-process for plotting:
#Get results table for Constipation coefficient relative to High Normal BMF:
re_const <- topTable(fit, coef = 2, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 2
re_low <- topTable(fit, coef = 3, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 3
re_diarrhea <- topTable(fit, coef = 4, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 4

indices <- match(rownames(re_const), rownames(meta)) # associate column of protein names with index of protein
re_const[1] <- countdf$name[indices] # associate the protein names with the protein indices
p_const <- re_const[re_const$adj.P.Val < 0.05,] # create df of just significant adj P value results
p_const <- p_const[order(p_const$adj.P.Val),] # order by adj P value
p_const <- p_const[,c('logFC','B','adj.P.Val','ID','P.Value')] # keep only desired columns

#Pre-process for plotting:
#Get results table for Low Normal coefficient relative to High Normal BMF:
re_low <- topTable(fit, coef = 3, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 3

indices <- match(rownames(re_low), rownames(meta)) # associate column of protein names with index of protein
re_low[1] <- countdf$name[indices] # associate the protein names with the protein indices
p_low <- re_low[re_low$adj.P.Val < 0.05,] # create df of just significant adj P value results
p_low <- p_low[order(p_low$adj.P.Val),] # order by adj P value
p_low <- p_low[,c('logFC','B','adj.P.Val','ID','P.Value')] # keep only desired columns

#Pre-process for plotting:
#Get results table for Diarrhea coefficient relative to High Normal BMF:
re_diarrhea <- topTable(fit, coef = 4, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 4

indices <- match(rownames(re_diarrhea), rownames(meta)) # associate column of protein names with index of protein
re_diarrhea[1] <- countdf$name[indices] # associate the protein names with the protein indices values
p_diarrhea <- re_diarrhea[re_diarrhea$adj.P.Val < 0.05,] # create df of just significant adj P value results
p_diarrhea <- p_diarrhea[order(p_diarrhea$adj.P.Val),] # order by adj P value
p_diarrhea <- p_diarrhea[,c('logFC','B','adj.P.Val','ID','P.Value')] # keep only desired columns



#Show dfs of significant hits (there are none)
sig_const <- p_const[which(p_const$adj.P.Val < 0.05),]
sig_low <- p_low[which(p_low$adj.P.Val < 0.05),]
sig_diarrhea <- p_diarrhea[which(p_diarrhea$adj.P.Val < 0.05),]
sig_low$bowel <- 'Low Normal'
sig_low
#Main Text Figures
comparisons = list(c("Constipation","High Normal"),c("Low Normal","High Normal"),c("Diarrhea","High Normal"))
titles = list(c('OMEGA-6/OMEGA-3 RATIO'),c('EPA'),c('HOMOCYSTEINE, SERUM'),c('EOSINOPHILS'))

myplots_main <- list()  # new empty list
#annotated only
labs_testanno <- cbind(full[,1],full[,6],as.data.frame(clr(full[,7:ncol(full)][,as.numeric(rownames(sig_low))])))
names(labs_testanno)[1] <- 'public_client_id'
names(labs_testanno)[2] <- 'bowel'
labs_testanno$bowel <- factor(labs_testanno$bowel, levels=c(1,2,3,4), labels = c("Constipation","Low Normal","High Normal","Diarrhea"))
labs_testanno$bowel <- factor(labs_testanno$bowel, levels=c("Constipation","Low Normal","High Normal","Diarrhea"), labels = c("Constipation","Low Normal","High Normal","Diarrhea"))


counter<<-1

sig = function(x){
  if(x < 0.001){"***"} 
  else if(x < 0.01){"**"}
  else if(x < 0.05){"*"}
  else{NA}}

test = function(x,y,z,j) {
  x = NULL
  y = NULL
  
  temp<- names(labs_testanno)[[j+2]]
  temp_size <- merge(labs_testanno[,c(1,2,j+2)],labs_testanno[,c('public_client_id','bowel')])
  temp_size <- temp_size[,c('bowel',temp)]
  temp_size <- temp_size[which(!is.na(temp_size)),]
  temp_size <- length(levels(factor(temp_size$bowel)))
  
  if (counter==1) {
    results = ifelse(nrow(sig_low[which(sig_low['bowel'] == 'Constipation'),]['adj.P.Val'])!=0,
           list(p.value = sig_low[which(sig_low['bowel'] == 'Constipation'),]['adj.P.Val'][[1]]),
           list(p.value = 1))
  }
  else if (counter==2) {
    results = ifelse(nrow(sig_low[which(sig_low['bowel'] == 'Low Normal'),]['adj.P.Val'])!=0,
           list(p.value = sig_low[which(sig_low['bowel'] == 'Low Normal'),]['adj.P.Val'][[1]]),
           list(p.value = 1))
  }
  else {
     results = ifelse(nrow(sig_low[which(sig_low['bowel'] == 'Diarrhea'),]['adj.P.Val'])!=0,
           list(p.value = sig_low[which(sig_low['bowel'] == 'Diarrhea'),]['adj.P.Val'][[1]]),
           list(p.value = 1))
  }
  
  if (temp_size == 2) { #if the # of levels to the metabolite is 2 categories (incl High Normal)
    counter<<-1
    }
  
  if (temp_size == 3) { #if the # of levels to the metabolite is 3 categories (incl High Normal)
    counter<<-counter+1
    if (counter > 2) {counter<<-1}
  }
  
  if (temp_size == 4) { #if the # of levels to the metabolite is all 4 categories (incl High Normal)
    counter<<-counter+1
    if (counter > 3) {counter<<-1}
  }
  
  names(results) <- 'p.value'
  return(results)
}

for (ind in 1:nrow(sig_low)) {
  y_name <- paste(names(labs_testanno)[c(ind+2)],sep="")
  myplots_main[[ind]] <- local({
    plotlim_loweranno = min(labs_testanno[[y_name]])
    plotlim_upperanno = max(labs_testanno[[y_name]])
    plotlim_baranno = plotlim_loweranno - 3.5
    plotlim_marginanno = plotlim_loweranno - 5
    labs_testanno <- labs_testanno[,c('bowel','public_client_id',y_name)]
    plt_anno <- ggplot(data = labs_testanno, aes(x = bowel, y = .data[[y_name]], group = bowel)) +
    scale_x_discrete(guide = guide_axis(n.dodge = 2))+
    geom_jitter(aes(color = bowel),  size = 0.1, cex = 0.05) +
    geom_boxplot(alpha=0.0,outlier.shape = NA) +
    theme(text = element_text(size = 9)) +
    ggtitle(label = str_wrap(titles[[ind]], width = 2)) +
    geom_signif(comparisons = comparisons, map_signif_level = sig, test = 'test', test.args = list(z = comparisons, j = ind), 
                y_position = plotlim_baranno, 
                step_increase = 0.15,  size = 0.5, 
                textsize = 1.5,
                tip_length = c(0,0)) +
    coord_cartesian(ylim=c(plotlim_loweranno,plotlim_upperanno),clip="off")+
    labs(color = "BMF Category", y = ifelse(ind == 1, "Log-Transformed\n Clinical Labs Level",""))+
    guides(colour = guide_legend(override.aes = list(size=7), title.position = 'left', nrow = 1, ncol = 4)) +
      
      
    theme(plot.margin = unit(c(0,0,abs(plotlim_marginanno),0), "cm"),
          legend.title = element_text(size=10), 
          legend.text = element_text(size=7),
          axis.text.x = element_blank(), 
          axis.title.x = element_blank()) +
      
    scale_fill_manual(limits = c("Constipation","Low Normal","High Normal","Diarrhea"), labels = c("Constipation","Low Normal","High Normal","Diarrhea"), values = colors(),
                    drop = FALSE)
  })
}
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
counter <<- 1
figure_main <- ggarrange(plotlist = myplots_main[1:length(myplots_main)], labels = LETTERS[1:4], legend = "top", align = "hv", common.legend = TRUE, nrow = 1, ncol = 4)
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
figure_main


ggsave(
  "BMFvsLabsMain.png",
  plot = figure_main,
  device = NULL,
  path = NULL,
  scale = 1.2,
  width = NA,
  height = NA,
  units = c("in", "cm", "mm", "px"),
  dpi = 300,
  limitsize = TRUE,
  bg = NULL
)
Saving 8.75 x 5.41 in image

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUHJvdGVvbWljcyBMSU1NQSBSZWdyZXNzaW9uIC0gSmFtZXMgSm9obnNvbiAtIHYxLTE4LTIyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiAKClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgcGxhY2luZyB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDdHJsK1NoaWZ0K0VudGVyKi4gCgpgYGB7cn0KI0xvYWQgc29tZSB1c2VmdWwgbGlicmFyaWVzCmxpYnJhcnkobGltbWEpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KGVkZ2VSKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNqbGFiZWxsZWQpCmxpYnJhcnkoc2ptaXNjKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY29tcG9zaXRpb25zKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KERhdGFDb21iaW5lKQpsaWJyYXJ5KGdnc2lnbmlmKQpsaWJyYXJ5KGdncHVicikKYGBgCgoKYGBge3J9CiNpbXBvcnQgZGF0YQpjb3VudGRmIDwtIHJlYWQuY3N2KGZpbGUgPSAnbGFicy5jc3YnKQpyb3duYW1lcyhjb3VudGRmKSA8LSBOVUxMCm1ldGEgPC0gcmVhZC5jc3YoZmlsZSA9ICdsYWJzX21ldGFkYXRhLmNzdicpCnJvd25hbWVzKG1ldGEpIDwtIE5VTEwKZnVsbCA8LSByZWFkLmNzdihmaWxlID0gJ2xhYnNfZnVsbC5jc3YnKQpyb3duYW1lcyhmdWxsKSA8LSBOVUxMCgojY2xlYW4tdXAgdGFibGVzCiNwcmVwYXJlIGZvciBtb2RlbAojbWFrZSBjb25zaXN0ZW50IGluZGV4L2NvbHVtbi9sYWJlbC9vcmRlcmluZyBiZXR3ZWVuIHRhYmxlcy9tYXRyaWNlcwpuYW1lcyhjb3VudGRmKVsxXSA8LSAnbmFtZScKY291bnRkZltjb3VudGRmPT0wXSA8LSAwLjAwMDAwMDEKbmFtZXMoY291bnRkZikgPC0gc2FwcGx5KHN0cl9yZW1vdmVfYWxsKG5hbWVzKGNvdW50ZGYpLCJYIiksIlsiKSAjIHJlbW92ZSBhZGRlZCBYJ3MgZnJvbSBQQ0kgbmFtZXMKY291bnRfbWF0cml4IDwtIGNvdW50ZGYKY291bnRfbWF0cml4IDwtIGNvdW50X21hdHJpeFssMjpuY29sKGNvdW50X21hdHJpeCldCmNvdW50X21hdHJpeCA8LSBhcy5tYXRyaXgoY291bnRfbWF0cml4KQpuYW1lcyhmdWxsKSA8LSBzYXBwbHkoc3RyX3JlbW92ZV9hbGwoY29sbmFtZXMoZnVsbCksIlgiKSwiWyIpCm5hbWVzKGNvdW50X21hdHJpeCkgPC0gc2FwcGx5KHN0cl9yZW1vdmVfYWxsKGNvbG5hbWVzKGNvdW50X21hdHJpeCksIlgiKSwiWyIpCm1ldGEkc2V4IDwtIGZhY3RvcihtZXRhJHNleCkKZnVsbCRzZXggPC0gZmFjdG9yKGZ1bGwkc2V4KQptZXRhJGJvd2VsIDwtIGZhY3Rvcihhcy5udW1lcmljKGZhY3RvcihtZXRhJGJvd2VsLCBsZXZlbHMgPSBjKDEsMiwzLDQpLCBsYWJlbHMgPSBjKDEsMiwzLDQpKSkpCmZ1bGwkYm93ZWwgPC0gZmFjdG9yKGFzLm51bWVyaWMoZmFjdG9yKGZ1bGwkYm93ZWwsIGxldmVscyA9IGMoMSwyLDMsNCksIGxhYmVscyA9IGMoMSwyLDMsNCkpKSkKbWV0YSA8LSB3aXRoaW4obWV0YSwgYm93ZWwgPC0gcmVsZXZlbChib3dlbCwgcmVmID0gMykpCmZ1bGwgPC0gd2l0aGluKGZ1bGwsIGJvd2VsIDwtIHJlbGV2ZWwoYm93ZWwsIHJlZiA9IDMpKQphcy5kYXRhLmZyYW1lKGNvdW50X21hdHJpeCkKY291bnRkZiA8LSBEcm9wTkEoY291bnRkZikKbWV0YSA8LSBEcm9wTkEobWV0YSkKZnVsbCA8LSBEcm9wTkEoZnVsbCkKY291bnRkZgptZXRhCmZ1bGwKYGBgCgoKYGBge3J9CiNkZXNpZ24gR0xNIHVzaW5nIGxtRml0IGFuZCBlQmF5ZXMKI0FsZ29yaXRobSBwcm92aWRlZCBieSBhbmQgYWRhcHRlZCBmcm9tIENocmlzdGlhbiBEaWVuZXIsIFBoRDoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+Ym93ZWwgKyBzZXggKyBhZ2UgKyBCTUlfQ0FMQyArIGVHRlIsIGZ1bGwpICAjIG5vdGUgdGhlIHNhbWUgZm9ybXVsYSBhcyBpbiBjb3JuY29iCmRnZSA8LSBER0VMaXN0KGNvdW50cz1jb3VudF9tYXRyaXgpICAjIHdoZXJlIGBjb3VudF9tYXRyaXhgIGlzIHRoZSBtYXRyaXggbWVudGlvbmVkIGFib3ZlCiNkZ2UgPC0gY2FsY05vcm1GYWN0b3JzKGRnZSkgICMgTm9ybWFsaXplIHRoZSBtYXRyaXggKHRoaXMgc3RlcCBvbmx5IGZvciBDT1JOQ09CL21pY3JvYmlvbWUgZGF0YSkKbG9nQ1BNIDwtIGNwbShkZ2UsIGxvZz1UUlVFKSAgIyB0YWtlcyB0aGUgbG9nIG9mIHRoZSBkYXRhCmZpdCA8LSBsbUZpdChsb2dDUE0sIGRlc2lnbikgICMgZml0cyB0aGUgbW9kZWwgZm9yIGFsbCBsYWJzCmZpdCA8LSBlQmF5ZXMoZml0KSAgIyBzdGFiaWxpemVzIHRoZSB2YXJpYW5jZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKYGBgCgoKYGBge3J9CiNQcmUtcHJvY2VzcyBmb3IgcGxvdHRpbmc6CiNHZXQgcmVzdWx0cyB0YWJsZSBmb3IgQ29uc3RpcGF0aW9uIGNvZWZmaWNpZW50IHJlbGF0aXZlIHRvIEhpZ2ggTm9ybWFsIEJNRjoKcmVfY29uc3QgPC0gdG9wVGFibGUoZml0LCBjb2VmID0gMiwgZ2VuZWxpc3QgPSBjb3VudGRmJG5hbWUsIHNvcnQ9InAiLCBudW1iZXI9Im5vbmUiKSAgIyBTZWxlY3QgdGhlIHNpZ25pZmljYW50IG1vZGVscyBieSBjb2VmZmljaWVudCAyCnJlX2xvdyA8LSB0b3BUYWJsZShmaXQsIGNvZWYgPSAzLCBnZW5lbGlzdCA9IGNvdW50ZGYkbmFtZSwgc29ydD0icCIsIG51bWJlcj0ibm9uZSIpICAjIFNlbGVjdCB0aGUgc2lnbmlmaWNhbnQgbW9kZWxzIGJ5IGNvZWZmaWNpZW50IDMKcmVfZGlhcnJoZWEgPC0gdG9wVGFibGUoZml0LCBjb2VmID0gNCwgZ2VuZWxpc3QgPSBjb3VudGRmJG5hbWUsIHNvcnQ9InAiLCBudW1iZXI9Im5vbmUiKSAgIyBTZWxlY3QgdGhlIHNpZ25pZmljYW50IG1vZGVscyBieSBjb2VmZmljaWVudCA0CgppbmRpY2VzIDwtIG1hdGNoKHJvd25hbWVzKHJlX2NvbnN0KSwgcm93bmFtZXMobWV0YSkpICMgYXNzb2NpYXRlIGNvbHVtbiBvZiBwcm90ZWluIG5hbWVzIHdpdGggaW5kZXggb2YgcHJvdGVpbgpyZV9jb25zdFsxXSA8LSBjb3VudGRmJG5hbWVbaW5kaWNlc10gIyBhc3NvY2lhdGUgdGhlIHByb3RlaW4gbmFtZXMgd2l0aCB0aGUgcHJvdGVpbiBpbmRpY2VzCnBfY29uc3QgPC0gcmVfY29uc3RbcmVfY29uc3QkYWRqLlAuVmFsIDwgMC4wNSxdICMgY3JlYXRlIGRmIG9mIGp1c3Qgc2lnbmlmaWNhbnQgYWRqIFAgdmFsdWUgcmVzdWx0cwpwX2NvbnN0IDwtIHBfY29uc3Rbb3JkZXIocF9jb25zdCRhZGouUC5WYWwpLF0gIyBvcmRlciBieSBhZGogUCB2YWx1ZQpwX2NvbnN0IDwtIHBfY29uc3RbLGMoJ2xvZ0ZDJywnQicsJ2Fkai5QLlZhbCcsJ0lEJywnUC5WYWx1ZScpXSAjIGtlZXAgb25seSBkZXNpcmVkIGNvbHVtbnMKCiNQcmUtcHJvY2VzcyBmb3IgcGxvdHRpbmc6CiNHZXQgcmVzdWx0cyB0YWJsZSBmb3IgTG93IE5vcm1hbCBjb2VmZmljaWVudCByZWxhdGl2ZSB0byBIaWdoIE5vcm1hbCBCTUY6CnJlX2xvdyA8LSB0b3BUYWJsZShmaXQsIGNvZWYgPSAzLCBnZW5lbGlzdCA9IGNvdW50ZGYkbmFtZSwgc29ydD0icCIsIG51bWJlcj0ibm9uZSIpICAjIFNlbGVjdCB0aGUgc2lnbmlmaWNhbnQgbW9kZWxzIGJ5IGNvZWZmaWNpZW50IDMKCmluZGljZXMgPC0gbWF0Y2gocm93bmFtZXMocmVfbG93KSwgcm93bmFtZXMobWV0YSkpICMgYXNzb2NpYXRlIGNvbHVtbiBvZiBwcm90ZWluIG5hbWVzIHdpdGggaW5kZXggb2YgcHJvdGVpbgpyZV9sb3dbMV0gPC0gY291bnRkZiRuYW1lW2luZGljZXNdICMgYXNzb2NpYXRlIHRoZSBwcm90ZWluIG5hbWVzIHdpdGggdGhlIHByb3RlaW4gaW5kaWNlcwpwX2xvdyA8LSByZV9sb3dbcmVfbG93JGFkai5QLlZhbCA8IDAuMDUsXSAjIGNyZWF0ZSBkZiBvZiBqdXN0IHNpZ25pZmljYW50IGFkaiBQIHZhbHVlIHJlc3VsdHMKcF9sb3cgPC0gcF9sb3dbb3JkZXIocF9sb3ckYWRqLlAuVmFsKSxdICMgb3JkZXIgYnkgYWRqIFAgdmFsdWUKcF9sb3cgPC0gcF9sb3dbLGMoJ2xvZ0ZDJywnQicsJ2Fkai5QLlZhbCcsJ0lEJywnUC5WYWx1ZScpXSAjIGtlZXAgb25seSBkZXNpcmVkIGNvbHVtbnMKCiNQcmUtcHJvY2VzcyBmb3IgcGxvdHRpbmc6CiNHZXQgcmVzdWx0cyB0YWJsZSBmb3IgRGlhcnJoZWEgY29lZmZpY2llbnQgcmVsYXRpdmUgdG8gSGlnaCBOb3JtYWwgQk1GOgpyZV9kaWFycmhlYSA8LSB0b3BUYWJsZShmaXQsIGNvZWYgPSA0LCBnZW5lbGlzdCA9IGNvdW50ZGYkbmFtZSwgc29ydD0icCIsIG51bWJlcj0ibm9uZSIpICAjIFNlbGVjdCB0aGUgc2lnbmlmaWNhbnQgbW9kZWxzIGJ5IGNvZWZmaWNpZW50IDQKCmluZGljZXMgPC0gbWF0Y2gocm93bmFtZXMocmVfZGlhcnJoZWEpLCByb3duYW1lcyhtZXRhKSkgIyBhc3NvY2lhdGUgY29sdW1uIG9mIHByb3RlaW4gbmFtZXMgd2l0aCBpbmRleCBvZiBwcm90ZWluCnJlX2RpYXJyaGVhWzFdIDwtIGNvdW50ZGYkbmFtZVtpbmRpY2VzXSAjIGFzc29jaWF0ZSB0aGUgcHJvdGVpbiBuYW1lcyB3aXRoIHRoZSBwcm90ZWluIGluZGljZXMgdmFsdWVzCnBfZGlhcnJoZWEgPC0gcmVfZGlhcnJoZWFbcmVfZGlhcnJoZWEkYWRqLlAuVmFsIDwgMC4wNSxdICMgY3JlYXRlIGRmIG9mIGp1c3Qgc2lnbmlmaWNhbnQgYWRqIFAgdmFsdWUgcmVzdWx0cwpwX2RpYXJyaGVhIDwtIHBfZGlhcnJoZWFbb3JkZXIocF9kaWFycmhlYSRhZGouUC5WYWwpLF0gIyBvcmRlciBieSBhZGogUCB2YWx1ZQpwX2RpYXJyaGVhIDwtIHBfZGlhcnJoZWFbLGMoJ2xvZ0ZDJywnQicsJ2Fkai5QLlZhbCcsJ0lEJywnUC5WYWx1ZScpXSAjIGtlZXAgb25seSBkZXNpcmVkIGNvbHVtbnMKCgoKI1Nob3cgZGZzIG9mIHNpZ25pZmljYW50IGhpdHMgKHRoZXJlIGFyZSBub25lKQpzaWdfY29uc3QgPC0gcF9jb25zdFt3aGljaChwX2NvbnN0JGFkai5QLlZhbCA8IDAuMDUpLF0Kc2lnX2xvdyA8LSBwX2xvd1t3aGljaChwX2xvdyRhZGouUC5WYWwgPCAwLjA1KSxdCnNpZ19kaWFycmhlYSA8LSBwX2RpYXJyaGVhW3doaWNoKHBfZGlhcnJoZWEkYWRqLlAuVmFsIDwgMC4wNSksXQpzaWdfbG93JGJvd2VsIDwtICdMb3cgTm9ybWFsJwpzaWdfbG93CmBgYAoKCgpgYGB7cn0KI01haW4gVGV4dCBGaWd1cmVzCmNvbXBhcmlzb25zID0gbGlzdChjKCJDb25zdGlwYXRpb24iLCJIaWdoIE5vcm1hbCIpLGMoIkxvdyBOb3JtYWwiLCJIaWdoIE5vcm1hbCIpLGMoIkRpYXJyaGVhIiwiSGlnaCBOb3JtYWwiKSkKdGl0bGVzID0gbGlzdChjKCdPTUVHQS02L09NRUdBLTMgUkFUSU8nKSxjKCdFUEEnKSxjKCdIT01PQ1lTVEVJTkUsIFNFUlVNJyksYygnRU9TSU5PUEhJTFMnKSkKCm15cGxvdHNfbWFpbiA8LSBsaXN0KCkgICMgbmV3IGVtcHR5IGxpc3QKI2Fubm90YXRlZCBvbmx5CmxhYnNfdGVzdGFubm8gPC0gY2JpbmQoZnVsbFssMV0sZnVsbFssNl0sYXMuZGF0YS5mcmFtZShjbHIoZnVsbFssNzpuY29sKGZ1bGwpXVssYXMubnVtZXJpYyhyb3duYW1lcyhzaWdfbG93KSldKSkpCm5hbWVzKGxhYnNfdGVzdGFubm8pWzFdIDwtICdwdWJsaWNfY2xpZW50X2lkJwpuYW1lcyhsYWJzX3Rlc3Rhbm5vKVsyXSA8LSAnYm93ZWwnCmxhYnNfdGVzdGFubm8kYm93ZWwgPC0gZmFjdG9yKGxhYnNfdGVzdGFubm8kYm93ZWwsIGxldmVscz1jKDEsMiwzLDQpLCBsYWJlbHMgPSBjKCJDb25zdGlwYXRpb24iLCJMb3cgTm9ybWFsIiwiSGlnaCBOb3JtYWwiLCJEaWFycmhlYSIpKQpsYWJzX3Rlc3Rhbm5vJGJvd2VsIDwtIGZhY3RvcihsYWJzX3Rlc3Rhbm5vJGJvd2VsLCBsZXZlbHM9YygiQ29uc3RpcGF0aW9uIiwiTG93IE5vcm1hbCIsIkhpZ2ggTm9ybWFsIiwiRGlhcnJoZWEiKSwgbGFiZWxzID0gYygiQ29uc3RpcGF0aW9uIiwiTG93IE5vcm1hbCIsIkhpZ2ggTm9ybWFsIiwiRGlhcnJoZWEiKSkKCgpjb3VudGVyPDwtMQoKc2lnID0gZnVuY3Rpb24oeCl7CiAgaWYoeCA8IDAuMDAxKXsiKioqIn0gCiAgZWxzZSBpZih4IDwgMC4wMSl7IioqIn0KICBlbHNlIGlmKHggPCAwLjA1KXsiKiJ9CiAgZWxzZXtOQX19Cgp0ZXN0ID0gZnVuY3Rpb24oeCx5LHosaikgewogIHggPSBOVUxMCiAgeSA9IE5VTEwKICAKICB0ZW1wPC0gbmFtZXMobGFic190ZXN0YW5ubylbW2orMl1dCiAgdGVtcF9zaXplIDwtIG1lcmdlKGxhYnNfdGVzdGFubm9bLGMoMSwyLGorMildLGxhYnNfdGVzdGFubm9bLGMoJ3B1YmxpY19jbGllbnRfaWQnLCdib3dlbCcpXSkKICB0ZW1wX3NpemUgPC0gdGVtcF9zaXplWyxjKCdib3dlbCcsdGVtcCldCiAgdGVtcF9zaXplIDwtIHRlbXBfc2l6ZVt3aGljaCghaXMubmEodGVtcF9zaXplKSksXQogIHRlbXBfc2l6ZSA8LSBsZW5ndGgobGV2ZWxzKGZhY3Rvcih0ZW1wX3NpemUkYm93ZWwpKSkKICAKICBpZiAoY291bnRlcj09MSkgewogICAgcmVzdWx0cyA9IGlmZWxzZShucm93KHNpZ19sb3dbd2hpY2goc2lnX2xvd1snYm93ZWwnXSA9PSAnQ29uc3RpcGF0aW9uJyksXVsnYWRqLlAuVmFsJ10pIT0wLAogICAgICAgICAgIGxpc3QocC52YWx1ZSA9IHNpZ19sb3dbd2hpY2goc2lnX2xvd1snYm93ZWwnXSA9PSAnQ29uc3RpcGF0aW9uJyksXVsnYWRqLlAuVmFsJ11bWzFdXSksCiAgICAgICAgICAgbGlzdChwLnZhbHVlID0gMSkpCiAgfQogIGVsc2UgaWYgKGNvdW50ZXI9PTIpIHsKICAgIHJlc3VsdHMgPSBpZmVsc2UobnJvdyhzaWdfbG93W3doaWNoKHNpZ19sb3dbJ2Jvd2VsJ10gPT0gJ0xvdyBOb3JtYWwnKSxdWydhZGouUC5WYWwnXSkhPTAsCiAgICAgICAgICAgbGlzdChwLnZhbHVlID0gc2lnX2xvd1t3aGljaChzaWdfbG93Wydib3dlbCddID09ICdMb3cgTm9ybWFsJyksXVsnYWRqLlAuVmFsJ11bWzFdXSksCiAgICAgICAgICAgbGlzdChwLnZhbHVlID0gMSkpCiAgfQogIGVsc2UgewogICAgIHJlc3VsdHMgPSBpZmVsc2UobnJvdyhzaWdfbG93W3doaWNoKHNpZ19sb3dbJ2Jvd2VsJ10gPT0gJ0RpYXJyaGVhJyksXVsnYWRqLlAuVmFsJ10pIT0wLAogICAgICAgICAgIGxpc3QocC52YWx1ZSA9IHNpZ19sb3dbd2hpY2goc2lnX2xvd1snYm93ZWwnXSA9PSAnRGlhcnJoZWEnKSxdWydhZGouUC5WYWwnXVtbMV1dKSwKICAgICAgICAgICBsaXN0KHAudmFsdWUgPSAxKSkKICB9CiAgCiAgaWYgKHRlbXBfc2l6ZSA9PSAyKSB7ICNpZiB0aGUgIyBvZiBsZXZlbHMgdG8gdGhlIG1ldGFib2xpdGUgaXMgMiBjYXRlZ29yaWVzIChpbmNsIEhpZ2ggTm9ybWFsKQogICAgY291bnRlcjw8LTEKICAgIH0KICAKICBpZiAodGVtcF9zaXplID09IDMpIHsgI2lmIHRoZSAjIG9mIGxldmVscyB0byB0aGUgbWV0YWJvbGl0ZSBpcyAzIGNhdGVnb3JpZXMgKGluY2wgSGlnaCBOb3JtYWwpCiAgICBjb3VudGVyPDwtY291bnRlcisxCiAgICBpZiAoY291bnRlciA+IDIpIHtjb3VudGVyPDwtMX0KICB9CiAgCiAgaWYgKHRlbXBfc2l6ZSA9PSA0KSB7ICNpZiB0aGUgIyBvZiBsZXZlbHMgdG8gdGhlIG1ldGFib2xpdGUgaXMgYWxsIDQgY2F0ZWdvcmllcyAoaW5jbCBIaWdoIE5vcm1hbCkKICAgIGNvdW50ZXI8PC1jb3VudGVyKzEKICAgIGlmIChjb3VudGVyID4gMykge2NvdW50ZXI8PC0xfQogIH0KICAKICBuYW1lcyhyZXN1bHRzKSA8LSAncC52YWx1ZScKICByZXR1cm4ocmVzdWx0cykKfQoKZm9yIChpbmQgaW4gMTpucm93KHNpZ19sb3cpKSB7CiAgeV9uYW1lIDwtIHBhc3RlKG5hbWVzKGxhYnNfdGVzdGFubm8pW2MoaW5kKzIpXSxzZXA9IiIpCiAgbXlwbG90c19tYWluW1tpbmRdXSA8LSBsb2NhbCh7CiAgICBwbG90bGltX2xvd2VyYW5ubyA9IG1pbihsYWJzX3Rlc3Rhbm5vW1t5X25hbWVdXSkKICAgIHBsb3RsaW1fdXBwZXJhbm5vID0gbWF4KGxhYnNfdGVzdGFubm9bW3lfbmFtZV1dKQogICAgcGxvdGxpbV9iYXJhbm5vID0gcGxvdGxpbV9sb3dlcmFubm8gLSAzLjUKICAgIHBsb3RsaW1fbWFyZ2luYW5ubyA9IHBsb3RsaW1fbG93ZXJhbm5vIC0gNQogICAgbGFic190ZXN0YW5ubyA8LSBsYWJzX3Rlc3Rhbm5vWyxjKCdib3dlbCcsJ3B1YmxpY19jbGllbnRfaWQnLHlfbmFtZSldCiAgICBwbHRfYW5ubyA8LSBnZ3Bsb3QoZGF0YSA9IGxhYnNfdGVzdGFubm8sIGFlcyh4ID0gYm93ZWwsIHkgPSAuZGF0YVtbeV9uYW1lXV0sIGdyb3VwID0gYm93ZWwpKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGd1aWRlID0gZ3VpZGVfYXhpcyhuLmRvZGdlID0gMikpKwogICAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gYm93ZWwpLCAgc2l6ZSA9IDAuMSwgY2V4ID0gMC4wNSkgKwogICAgZ2VvbV9ib3hwbG90KGFscGhhPTAuMCxvdXRsaWVyLnNoYXBlID0gTkEpICsKICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpKSArCiAgICBnZ3RpdGxlKGxhYmVsID0gc3RyX3dyYXAodGl0bGVzW1tpbmRdXSwgd2lkdGggPSAyKSkgKwogICAgZ2VvbV9zaWduaWYoY29tcGFyaXNvbnMgPSBjb21wYXJpc29ucywgbWFwX3NpZ25pZl9sZXZlbCA9IHNpZywgdGVzdCA9ICd0ZXN0JywgdGVzdC5hcmdzID0gbGlzdCh6ID0gY29tcGFyaXNvbnMsIGogPSBpbmQpLCAKICAgICAgICAgICAgICAgIHlfcG9zaXRpb24gPSBwbG90bGltX2JhcmFubm8sIAogICAgICAgICAgICAgICAgc3RlcF9pbmNyZWFzZSA9IDAuMTUsICBzaXplID0gMC41LCAKICAgICAgICAgICAgICAgIHRleHRzaXplID0gMS41LAogICAgICAgICAgICAgICAgdGlwX2xlbmd0aCA9IGMoMCwwKSkgKwogICAgY29vcmRfY2FydGVzaWFuKHlsaW09YyhwbG90bGltX2xvd2VyYW5ubyxwbG90bGltX3VwcGVyYW5ubyksY2xpcD0ib2ZmIikrCiAgICBsYWJzKGNvbG9yID0gIkJNRiBDYXRlZ29yeSIsIHkgPSBpZmVsc2UoaW5kID09IDEsICJMb2ctVHJhbnNmb3JtZWRcbiBDbGluaWNhbCBMYWJzIExldmVsIiwiIikpKwogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NyksIHRpdGxlLnBvc2l0aW9uID0gJ2xlZnQnLCBucm93ID0gMSwgbmNvbCA9IDQpKSArCiAgICAgIAogICAgICAKICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCxhYnMocGxvdGxpbV9tYXJnaW5hbm5vKSwwKSwgImNtIiksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwKICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKGxpbWl0cyA9IGMoIkNvbnN0aXBhdGlvbiIsIkxvdyBOb3JtYWwiLCJIaWdoIE5vcm1hbCIsIkRpYXJyaGVhIiksIGxhYmVscyA9IGMoIkNvbnN0aXBhdGlvbiIsIkxvdyBOb3JtYWwiLCJIaWdoIE5vcm1hbCIsIkRpYXJyaGVhIiksIHZhbHVlcyA9IGNvbG9ycygpLAogICAgICAgICAgICAgICAgICAgIGRyb3AgPSBGQUxTRSkKICB9KQp9Cgpjb3VudGVyIDw8LSAxCmZpZ3VyZV9tYWluIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IG15cGxvdHNfbWFpblsxOmxlbmd0aChteXBsb3RzX21haW4pXSwgbGFiZWxzID0gTEVUVEVSU1sxOjRdLCBsZWdlbmQgPSAidG9wIiwgYWxpZ24gPSAiaHYiLCBjb21tb24ubGVnZW5kID0gVFJVRSwgbnJvdyA9IDEsIG5jb2wgPSA0KQoKCmZpZ3VyZV9tYWluCgpnZ3NhdmUoCiAgIkJNRnZzTGFic01haW4ucG5nIiwKICBwbG90ID0gZmlndXJlX21haW4sCiAgZGV2aWNlID0gTlVMTCwKICBwYXRoID0gTlVMTCwKICBzY2FsZSA9IDEuMiwKICB3aWR0aCA9IE5BLAogIGhlaWdodCA9IE5BLAogIHVuaXRzID0gYygiaW4iLCAiY20iLCAibW0iLCAicHgiKSwKICBkcGkgPSAzMDAsCiAgbGltaXRzaXplID0gVFJVRSwKICBiZyA9IE5VTEwKKQpgYGAKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==